home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Revolution - Das Atari CD Magazin 1997
/
Revolution - Das Atari CD Magazin 1.iso
/
software
/
anwendng
/
qed_397
/
sourcen
/
memory.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-12-29
|
14KB
|
636 lines
/*
Ja, Du hast Recht. Einmal allozierter Speicher wird nicht wieder
freigegeben. Das hat mich auch schon immer gestoert, ich hatte aber
nie die Zeit daran mal was zu aendern.
QED holt sich vom System immer Bloecke einer bestimmten Groesse und
verwaltet sie dann selbst. Eine richtige garbage collection findet
nicht statt. Werden aber zwei Zeilen, die im Speicher hintereinander
gelegen haben wieder frei gegeben, werden sie wieder zu einem grossen
Block verschmolzen. Das erfolgt sehr effizient (und damit ist der Code
ziemlich grausam).
Alle freien Bloecke einer Groesse werden in einer verketteten Liste
gehalten. Die Anfaenge dieser Listen stehen in einem Array. Um bei der
freigabe einer Zeile aber nun schnell festzustennen, ob dahinter noch
ein anderer freier Block ist (und nicht erst alle Listen durchlaufen
zu muessen), mit dem der erste dann verschmolzen
werden kann, gibt es dieses MAGIC-Word. Ich sehe also direkt im
Speicher hinter dem ersten Block nach, ob dort das Magic-Word
steht. Wenn ja, dann liegt dort ein freier Block. Damit ich weiss um
einen Block welcher Groesse es sich handelt, steht dort auch noch die
Groessse und fuer eine einfache Umhaengung in den Listen auch gleich die
Zeiger auf Vorgaenger und Nachfolger in der Liste.
Lange Rede, kurzen Sinn: mit diesem Verfahren ist es moeglich in
*konstanter* Zeit eine Free-Operation mit Verschmelzung zu
realisieren.
Aber daran brauchst Du wohl garnichts aendern, wenn Du das Programm so
verbessern willst, dass es den Speicher ans System wieder
zurueckgibt. Du musst nur ueberpruefen, ob ein Block komplett frei
ist. Wenn das der Fall ist, musst Du alle kleinen Bloecke aus den
free_list-Listen herausnehemen und kannst dann den Block ans System
zureuckgeben.
*/
#include "global.h"
#include "qed.h"
#include "text.h"
#include "windows.h"
#include "scroll.h"
#undef MAGIC
#define MAGIC 0xFED1
#define MAX_ANZ 67
#define BLOCK_SIZE (MAX_ANZ*4)
#define BLOCKANZ 200 /* Für einen malloc */
#define MEMSIZE ((LONG)BLOCK_SIZE*BLOCKANZ) /* für einen malloc */
#define MEM_SIZE(x) (((x)+2+3)&(~3)) /* 2 Bytes drauf für den Kopf */
#define MEM_ADD(x,d) (BLOCK*)((char*)(x)+(d))
#define MEM_SUB(x,d) (BLOCK*)((char*)(x)-(d))
typedef union tblock{
struct{
UWORD magic; /* 2 Bytes */
UWORD size; /* 2 Bytes */
union tblock *next; /* 4 Bytes */
union tblock *prev; /* 4 Bytes => 12 Bytes */
}FREI;
struct{
UWORD size; /* 2 Bytes */
}USED;
} BLOCK;
LOCAL BOOLEAN mem_need_BS, /* BS hat keinen Speicher mehr */
mem_need_TQ; /* TQ hat keinen Speicher mehr */
LOCAL VOID* block_list;
LOCAL BLOCK* free_list[MAX_ANZ+1+1]; /* ein Dummy am Ende */
LOCAL VOID new_block(VOID)
{
BLOCK *adr, *adr2, *pre;
LONG *ptr;
LONG anz, i;
anz = (LONG)Malloc(-1L);
if (anz>=4+MEMSIZE+4)
{
ptr = (LONG*)Malloc(4+MEMSIZE+4);
anz -= 4+MEMSIZE+4;
}
else
ptr = NULL;
if (anz < MEMSIZE+20000L)
mem_need_BS = TRUE;
if (ptr == NULL)
{
if (anz>=4+BLOCK_SIZE+4)
{
i = anz = (anz-8)/(BLOCK_SIZE);
anz = 4+(anz*BLOCK_SIZE)+4;
ptr = (LONG*)Malloc(anz);
}
else
{
note(1, FATALMEM);
Pterm(1);
}
mem_need_TQ = TRUE;
}
else
{
i = BLOCKANZ;
mem_need_TQ = FALSE;
}
*(VOID**)ptr = block_list; /* In Liste einhängen */
block_list = ptr;
adr = MEM_ADD(ptr,4);
free_list[MAX_ANZ] = adr;
pre = NULL;
while ((--i)>0)
{
adr2 = MEM_ADD(adr,BLOCK_SIZE);
*(LONG*)&adr->FREI.magic = (LONG)(MAGIC<<16)+BLOCK_SIZE;
/* adr->FREI.magic = MAGIC; */
/* adr->FREI.size = BLOCK_SIZE; */
adr->FREI.next = adr2;
adr->FREI.prev = pre;
pre = adr;
adr = adr2;
}
adr2 = MEM_ADD(adr,BLOCK_SIZE);
*(LONG*)&adr->FREI.magic = (LONG)(MAGIC<<16)+BLOCK_SIZE;
/* adr->FREI.magic = MAGIC; */
/* adr->FREI.size = BLOCK_SIZE; */
adr->FREI.next = NULL;
adr->FREI.prev = pre;
*(LONG*)adr2 = 0L; /* damit bei FREE da kein MAGIC steht */
}
LOCAL VOID *MALLOC(UWORD size)
{
BLOCK *adr, **feld;
size = MEM_SIZE(size);
if (size>BLOCK_SIZE)
{
inote(1, FATALERR, 5);
size = BLOCK_SIZE;
}
feld = (BLOCK**)((char*)free_list+size);
if (*feld==NULL) /* kein passender */
{
if (size+16<=BLOCK_SIZE) /* größeren Block suchen und teilen */
{
BLOCK *adr2;
WORD i;
i = size+16; feld += 4; /* 16 Bytes kleinster Block zum Abspalten */
while (TRUE) /* größeren Block suchen */
{
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
if (*feld++!=NULL) break; i+=4;
}
feld--;
if (i==(BLOCK_SIZE+4)) /* kein Block vorhanden */
{
new_block();
feld--; i-=4;
}
adr = *feld; /* Zeiger auf Block */
*feld = adr->FREI.next; /* Aushängen */
if (*feld!=NULL) (*feld)->FREI.prev = NULL;
adr->USED.size = size;
/* Rest in die free_list einhängen */
adr2 = MEM_ADD(adr,size); /* Zeiger auf Restblock */
(char*)feld -= size;
i -= size; /* Restgröße */
adr2->FREI.magic = MAGIC; /* Einhängen */
adr2->FREI.size = i;
adr2->FREI.next = *feld;
adr2->FREI.prev = NULL;
if (*feld!=NULL) (*feld)->FREI.prev = adr2;
*feld = adr2;
}
else /* größten Block nehmen */
{
feld = (BLOCK**)(&free_list[MAX_ANZ]);
if (*feld==NULL) /* kein Block vorhanden */
new_block();
adr = *feld; /* Zeiger auf Block */
*feld = adr->FREI.next; /* Aushängen */
if (*feld!=NULL) (*feld)->FREI.prev = NULL;
adr->USED.size = BLOCK_SIZE;
}
}
else /* passend vorhanden */
{
adr = *feld; /* Zeiger auf Block */
*feld = adr->FREI.next; /* Aushängen */
if (*feld!=NULL) (*feld)->FREI.prev = NULL;
adr->USED.size = size;
}
if (free_list[MAX_ANZ]==NULL)
mem_need_TQ = TRUE;
return(MEM_ADD(adr,2));
}
LOCAL VOID FREE(VOID *adr)
{
WORD size1, size2;
BLOCK *adr1, *adr2, **feld;
adr1 = MEM_SUB(adr,2); /* adr1 auf ersten Block */
size1 = adr1->USED.size;
/*#ifdef CONCAT*/
adr2 = MEM_ADD(adr1,size1); /* adr2 auf Nachfolger */
size2 = adr2->FREI.size;
if (adr2->FREI.magic==MAGIC && (size1+size2<=BLOCK_SIZE))
/* freier Block dahinter => Zusammenfügen */
{
BLOCK *pre;
pre = adr2->FREI.prev; /* Aushängen */
if (pre==NULL) /* erster in der Liste */
*(BLOCK**)((char*)free_list+size2) = adr2->FREI.next;
else
pre->FREI.next = adr2->FREI.next;
if (adr2->FREI.next!=NULL) /* es gibt Nachfolger */
adr2->FREI.next->FREI.prev = pre;
size1 += size2;
}
/*#endif*/
feld = (BLOCK**)((char*)free_list+size1); /* Einhängen */
adr1->FREI.magic = MAGIC;
adr1->FREI.size = size1;
adr1->FREI.next = *feld;
adr1->FREI.prev = NULL;
if (*feld!=NULL) (*feld)->FREI.prev = adr1;
*feld = adr1;
if (size1==BLOCK_SIZE)
mem_need_TQ = FALSE;
}
/* =========================================================== */
VOID INSERT(LINEP *a, WORD pos, WORD delta, const char *str)
{
COPYB(REALLOC(a,pos,delta),str,delta);
}
/* =========================================================== */
char *REALLOC(LINEP *a, WORD pos, WORD delta)
{
LINEP col;
WORD new_len;
BLOCK *adr;
col = *a;
if (delta==0) return(TEXT(col)+pos);
new_len = col->len+delta;
if (new_len<0 || new_len>MAX_LINE_LEN)
note(1, FATALMEM);
adr = MEM_SUB(col,2);
if (MEM_SIZE((UWORD)sizeof(ZEILE)+new_len+1) != adr->USED.size)
{
LINEP new;
new = MALLOC( (short) sizeof(ZEILE) + new_len + 1);
COPYW(new, col, (short) sizeof(ZEILE)+pos);
if (delta<0)
{
new->len = new_len;
COPYB(TEXT(new)+pos,TEXT(col)+pos-delta,new_len-pos+1);
}
else
{
COPYB(TEXT(new)+pos+delta,TEXT(col)+pos,col->len-pos+1);
new->len = new_len;
}
FREE(col);
new->vorg->nachf = new;
new->nachf->vorg = new;
*a = new;
return(TEXT(new)+pos);
}
else /* Der Platzt in der Zeile reicht */
{
if (delta<0)
{
col->len = new_len;
COPYB(TEXT(col)+pos,TEXT(col)+pos-delta,new_len-pos+1);
}
else
{
MOVE(TEXT(col)+pos+delta,TEXT(col)+pos,col->len-pos+1);
col->len = new_len;
}
return(TEXT(col)+pos);
}
}
LINEP new_col_w(const char *str, WORD l)
{
LINEP a;
a = (LINEP)MALLOC( (short) sizeof(ZEILE)+l+1);
a->info = 0;
a->len = l;
COPYW(TEXT(a),str,l);
TEXT(a)[l] = EOS;
return(a);
}
LINEP new_col_b(const char *str, WORD l)
{
LINEP a;
a = (LINEP)MALLOC( (short)sizeof(ZEILE)+l+1);
a->info = 0;
a->len = l;
if ((WORD)str&1)
{
*(char*)COPYB(TEXT(a),str,l) = EOS;
}
else
{
COPYW(TEXT(a),str,l);
TEXT(a)[l] = EOS;
}
return(a);
}
VOID free_col(LINEP col)
{
FREE(col);
}
/* Zeile nach WO einfügen */
LINEP col_insert(LINEP wo, LINEP was)
{
LINEP help;
help = wo->nachf;
help->vorg = was;
was->nachf = help;
was->vorg = wo;
wo->nachf = was;
return was;
}
/* Zeile am Ende anhängen */
VOID col_append(RINGP t, LINEP was)
{
LINEP help;
help = LAST(t);
was->vorg = help;
was->nachf = help->nachf;
help->nachf = was;
t->tail.vorg = was;
t->lines++;
}
VOID col_delete(LINEP wo)
{
wo->vorg->nachf = wo->nachf;
wo->nachf->vorg = wo->vorg;
FREE(wo);
}
VOID col_concate(LINEP *wo)
{
LINEP help, col;
BOOLEAN absatz;
col = *wo;
help = col->nachf;
if (col->len)
{
absatz = help->info&ABSATZ;
INSERT(&col, col->len, help->len, TEXT(help));
help->nachf->vorg = col;
col->nachf = help->nachf;
FREE(help);
if (absatz)
col->info |= ABSATZ;
else
col->info &= ~ABSATZ;
*wo = col;
}
else
{
col->vorg->nachf = help;
help->vorg = col->vorg;
FREE(col);
*wo = help;
}
}
VOID col_split(LINEP *col,WORD pos)
{
LINEP new,help;
WORD anz;
BOOLEAN absatz;
help = *col;
absatz = help->info&ABSATZ;
help->info &= ~ABSATZ;
if (pos==0)
{
new = new_col_w ("",0);
col_insert(help->vorg,new);
*col = new;
}
else if (pos<help->len)
{
anz = help->len-pos;
new = new_col_b (TEXT(help)+pos, anz);
col_insert (help, new);
REALLOC(&help,pos,-anz);
*col = help;
}
else
{
new = new_col_w ("",0);
col_insert(help,new);
}
if (absatz) (*col)->nachf->info |= ABSATZ;
else (*col)->nachf->info &= ~ABSATZ;
}
LINEP get_line(RINGP r, LONG y)
{
LINEP lauf;
if (y<0 || y>=r->lines) return NULL;
if (y<(r->lines>>1))
{
lauf = FIRST(r);
while ((--y)>=0) NEXT(lauf);
}
else
{
lauf = LAST(r);
y = r->lines-y;
while ((--y)>0) VORG(lauf);
}
return lauf;
}
/*=========================================================================*/
VOID init_textring(RINGP r)
{
LINEP a;
r->head.info = HEAD;
r->tail.info = TAIL;
a = new_col_w("",0);
a->nachf = &r->tail;
a->vorg = &r->head;
LAST(r) = a;
r->tail.nachf = NULL;
FIRST(r) = a;
r->head.vorg = NULL;
r->lines = 1;
r->longest_len = 0;
}
LONG textring_bytes(RINGP r, LineEnding ending)
{
LINEP lauf, ende;
LONG bytes;
lauf = FIRST(r);
ende = LAST(r);
bytes = lauf->len;
while(lauf!=ende)
{
NEXT(lauf);
bytes += lauf->len;
}
/* plus Zeilenenden: 1 Zeichen für alle */
bytes += r->lines-1;
if (ending == tos)
/* für TOS noch ein Zeichen */
bytes += r->lines-1;
return bytes;
}
WORD get_longest_len(RINGP r)
{
/*
WORD max;
LINEP lauf;
max = 0;
lauf = FIRST(r);
if (lauf != NULL)
{
while (!IS_LAST(lauf))
{
if (lauf->len > max)
max = lauf->len;
NEXT(lauf);
}
r->longest_len = max;
}
return max;
*/
return 0;
}
VOID free_textring(RINGP r)
{
LINEP lauf, frei, ende;
frei = LAST(r); /* letzte Zeile */
lauf = frei->vorg; /* vorletzte Zeile */
ende = FIRST(r); /* erste Zeile */
while (frei!=ende)
{
FREE(frei);
frei = lauf;
VORG(lauf);
}
LAST(r) = frei;
frei->nachf = &r->tail;
REALLOC(&frei,0,-(frei->len));
frei->info = 0;
r->lines = 1;
}
VOID kill_textring(RINGP r)
{
free_textring(r);
FREE(FIRST(r));
FIRST(r) = NULL;
LAST(r) = NULL;
r->lines = 0;
}
BOOLEAN doppeln(RINGP old, RINGP new)
{
LINEP lauf, neu, a;
LONG lines, anz;
BOOLEAN erg;
erg = TRUE;
free_textring(new);
a = FIRST(new);
lauf = FIRST(old);
anz = old->lines;
INSERT(&a, 0, lauf->len, TEXT(lauf));
a->info = lauf->info;
NEXT(lauf);
NEXT(a);
lines = 1L;
while (lines<anz)
{
neu = new_col_w(TEXT(lauf),lauf->len);
neu->info = lauf->info; /* ABSATZ mit kopieren */
col_insert(a->vorg,neu);
NEXT(lauf);
lines++;
if (!ist_mem_frei())
{
erg = FALSE;
break;
}
}
new->lines = lines;
return erg;
}
BOOLEAN ist_leer(RINGP r)
{
return(r->lines==1 && FIRST(r)->len==0);
}
/*=========================================================================*/
VOID kill_memory(VOID)
{
WORD i;
BLOCK **ptr;
VOID *help;
for (i=MAX_ANZ+1,ptr=free_list; (--i)>=0; )
{
*ptr++ = NULL;
}
while (block_list!=NULL)
{
help = *(VOID**)block_list; /* nächster Block */
Mfree(block_list);
block_list = help;
}
mem_need_TQ = mem_need_BS = FALSE;
}
BOOLEAN is_mem_free(VOID)
{
return (!(mem_need_BS && mem_need_TQ));
}
BOOLEAN ist_mem_frei(VOID)
{
if (mem_need_BS && mem_need_TQ)
{
note(1,NOMEMORY);
return (FALSE);
}
else
return (TRUE);
}
VOID init_memory(VOID)
{
WORD i;
BLOCK **ptr;
mem_need_TQ = mem_need_BS = FALSE;
for (i=MAX_ANZ+1,ptr=free_list; (--i)>=0; )
*ptr++ = NULL;
*ptr = (VOID*)-1L; /* für schleifenende */
block_list = NULL;
}